#![feature(nll)]
#![feature(never_type)]
extern crate sdl2;
use sdl2::pixels::{Color};
use sdl2::event::{Event, WindowEvent};

pub mod geom;
pub mod frag;
pub mod diff;
pub mod tree;

/// Virtual trees used for this app
type V = tree::Virtual<frag::Frag, Sq>;

/// Abstract nodes used for this app
#[derive(Clone, Eq, PartialEq)]
struct Sq { key: u32, color: Color }

impl tree::Abstract<frag::Frag> for Sq {
  type State = u32;

  fn initial_state(&self) -> u32 {
    print!("build new state (key={})\n", self.key);
    40
  }

  fn render(&self, size: &u32) -> V {
    print!("render (key={})\n", self.key);
    let dim = geom::Dim(*size, *size);
    frag::rect(dim, self.color)
      .into()
  }

  fn compatible(&self, other: &Self) -> bool {
    self.key == other.key
  }

  fn should_update(&self, size: &u32, next: &Self, next_size: &u32) -> bool {
    *self != *next || *size != *next_size
  }
}

fn squares(i: usize) -> Vec<Sq> {
  let mut squares = vec![];
  if i < 30 {
    squares.push(Sq { key: 0, color: Color::RGB(255, 0, 40) });
  }
  if i < 20 {
    squares.push(Sq { key: 1, color: Color::RGB(0, 100, 200) });
  }
  if i < 10 {
    squares.push(Sq { key: 2, color: Color::RGB(255, 230, 0) });
  }
  squares
}

fn scene_virtual(squares: Vec<Sq>) -> V {
  use frag::{FragInfo};
  use geom::{Dim};
  use sdl2::pixels::{Color};
  use frag::FragSpine::*;
  use frag::AlignH::*;
  use frag::AlignV::*;

  V::branch(Container, FragInfo {
    bounds: Dim(600, 400),
    align_h: Center,
    align_v: Middle,
  }, vec![
    V::frag(frag::fill(Color::RGB(255, 255, 255))),
    V::branch(
      PackLR,
      FragInfo::with_align(Left, Top),
      squares.into_iter()
             .map(V::abs)
             .collect(),
    )
  ])
}

fn main() {
  let sdl = sdl2::init().unwrap();
  let mut events = sdl.event_pump().unwrap();

  let vid = sdl.video().unwrap();
  let mut canvas: sdl2::render::Canvas<_> =
    vid.window("R", 800, 600)
       .build()
       .unwrap()
       .into_canvas()
       .build()
       .unwrap();

  let mut tree = tree::Tree::default();
  let mut size = geom::Dim(800, 600);
  let mut need_update = true;
  let mut i: usize = 0;

  'renderloop: loop {
    for evt in events.poll_iter() {
      match evt {
        Event::Quit { .. } =>
          break 'renderloop,

        Event::Window { win_event: WindowEvent::Resized(w, h), .. } => {
          need_update = true;
          size = geom::Dim(w as u32, h as u32);
        }

        Event::MouseButtonDown { .. } => {
          need_update = true;
          let n = squares(i).len();
          tree.update(&[1, (i % n)], |&s| s + ((i%n+1)*8) as u32).unwrap();
          i += 1;
        }

        _ => (),
      }
    }

    if need_update {
      use std::time::{Instant};
      let start = Instant::now();
      let sqs = squares(i);
      tree.impose(scene_virtual(sqs));
      tree.underlying.calculate_geometry(size);
      print!("{:?}\n", Instant::now() - start);
      need_update = false;
    }

    {
      use frag::{DrawFrag};

      canvas.set_draw_color(Color::RGB(0, 0, 0));
      canvas.clear();
      canvas.draw_frag(&tree.underlying);
      canvas.present();
      std::thread::sleep(
        std::time::Duration::from_micros(1000000 / 64)
      );
    }
  }
}
